extern crate lazy_static;

use crate::entity::mock_router::MockRouter;
use anyhow::Error;
use lazy_static::lazy_static;
use sqlx::mysql::MySqlPoolOptions;
use std::collections::HashMap;
use tokio::sync::Mutex;

lazy_static! {
    pub static ref ROUTERS: Mutex<RouterMap> = Mutex::new(RouterMap::default());
    pub static ref DBSTR: std::sync::Mutex<String> =
        std::sync::Mutex::new("mysql://username:password@localhost:3306/database".to_string());
}

#[derive(Debug)]
pub struct RouterMap {
    pub dsn: String,
    pub routers: HashMap<String, MockRouter>,
}

impl RouterMap {
    pub fn new(dsn: &str) -> RouterMap {
        let rm = RouterMap {
            routers: HashMap::new(),
            dsn: dsn.to_owned(),
        };
        rm
    }
    pub fn search(&self, path: &str) -> Option<MockRouter> {
        match self.routers.get(path) {
            Some(r) => {
                let mut rr = MockRouter {
                    id: r.id,
                    route: r.route.clone(),
                    group: r.group.clone(),
                    method: r.method.clone(),
                    headers: HashMap::new(),
                    status_code: r.status_code,
                    content: r.content.clone(),
                };
                for (key, value) in r.headers.clone() {
                    rr.headers
                        .insert(key.clone().to_string(), value.clone().to_string());
                }
                Some(rr)
            }
            None => None,
        }
    }
    pub async fn update(&mut self) -> Result<(), Error> {
        let conn = MySqlPoolOptions::new().connect(&self.dsn).await?;
        let rows = sqlx::query(
            "select id,path,route_group,method,headers,status_code,content from mock_routers",
        )
        .fetch_all(&conn)
        .await?;
        for row in rows {
            let router: MockRouter = row.into();
            self.routers.insert(
                format!("/{}{}", router.group.clone(), router.route.clone()),
                router,
            );
        }
        Ok(())
    }
}

impl Default for RouterMap {
    fn default() -> Self {
        let dsn = DBSTR.lock().unwrap();
        RouterMap::new(dsn.clone().as_str())
    }
}

pub fn init_dsn(dsn: &str) {
    *DBSTR.lock().unwrap() = dsn.to_string();
}

mod tests {
    #[allow(unused_imports)]
    use super::*;
    #[test]
    fn test_router_map() {
        let rm = RouterMap::new(DBSTR.lock().unwrap().clone().as_str());
        assert_eq!(rm.routers.len(), 1);
    }
    #[tokio::test]
    async fn test_router_map_update() {
        let mut rm = RouterMap::new(DBSTR.lock().unwrap().clone().as_str());
        match rm.update().await {
            Ok(()) => {
                println!("{:?}", rm);
            }
            Err(e) => {
                println!("{}", e);
            }
        }
    }
}
